package com.briup.pai.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.briup.pai.common.constant.DatasetConstant;
import com.briup.pai.common.enums.ResultCodeEnum;
import com.briup.pai.common.exception.BriupAssert;
import com.briup.pai.convert.ClassifyConvert;
import com.briup.pai.dao.ClassifyMapper;
import com.briup.pai.entity.dto.ClassifySaveDTO;
import com.briup.pai.entity.po.Classify;
import com.briup.pai.entity.po.Entity;
import com.briup.pai.entity.vo.ClassifyEchoVO;
import com.briup.pai.entity.vo.ClassifyInDatasetVO;
import com.briup.pai.service.IClassifyService;
import com.briup.pai.service.IEntityService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.util.List;

@Service
@CacheConfig(cacheNames = DatasetConstant.DATASET_CACHE_PREFIX)
public class ClassifyServiceImpl extends ServiceImpl<ClassifyMapper, Classify> implements IClassifyService {

    @Autowired
    private ClassifyConvert classifyConvert;
    @Autowired
    private IEntityService entityService;
    @Value("${upload.nginx-file-path}")
    private String nginxFilePath;
    @Autowired
    private ModelServiceImpl modelServiceImpl;

    @Override
    public List<ClassifyInDatasetVO> getClassifiesByDatasetId(Integer datasetId) {
        // 获取数据集下的分类信息
        LambdaQueryWrapper<Classify> lqw = new LambdaQueryWrapper<>();
        lqw.eq(Classify::getDatasetId,datasetId);
        return classifyConvert.po2ClassifyInDatasetVOList(
                this.baseMapper.selectList(lqw))
                .stream()
                .peek(classifyInDatasetVO ->
                        classifyInDatasetVO.setEntityNum(
                                (long) entityService.getEntityByClassifyId(
                                        classifyInDatasetVO.getClassifyId()).size())).toList();
    }

    @Override
    @Transactional
    @CachePut(key = "T(com.briup.pai.common.constant.DatasetConstant).DATASET_CLASSIFY_CACHE_PREFIX+':'+#dto.getClassifyId()")
    @CacheEvict(key = "T(com.briup.pai.common.constant.CommonConstant).DETAIL_CACHE_PREFIX+':'+#dto.getDatasetId()")
    public ClassifyEchoVO addOrModifyClassify(ClassifySaveDTO dto) {
        // 参数校验
        Integer datasetId = dto.getDatasetId();
        String classifyName = dto.getClassifyName();
        BriupAssert.requireNotNull(datasetId, ResultCodeEnum.PARAM_IS_ERROR);
        BriupAssert.requireNotNull(classifyName, ResultCodeEnum.PARAM_IS_ERROR);
        Integer classifyId = dto.getClassifyId();
        // 从相同条件下查出来的数据集下的分类，判断分类名称是否重复 => 分类名称必须唯一
        LambdaQueryWrapper<Classify> lqw = new LambdaQueryWrapper<>();
        lqw.eq(Classify::getDatasetId,datasetId)
                .eq(Classify::getClassifyName,classifyName);
        BriupAssert.requireNull(
                this.getOne(lqw),
                ResultCodeEnum.DATA_ALREADY_EXIST);
        // 准备一个数据返回
        Classify classify = null;
        // 判断classifyId是否存在
        if(ObjectUtil.isEmpty(classifyId)){
            // 新增
            // 同步创建文件夹
            FileUtil.mkdir(nginxFilePath + "/" + datasetId + "/" + classifyName);
            // 添加
            classify = classifyConvert.classifySaveDTO2Po(dto);
            this.save(classify);
        }else {
            // 修改
            // 分类必须存在
            Classify temp = BriupAssert.requireNotNull(
                    this,
                    Classify::getId,
                    classifyId,
                    ResultCodeEnum.DATA_NOT_EXIST);
            // 数据集id不能修改
            BriupAssert.requireEqual(
                    temp.getDatasetId(),
                    datasetId,
                    ResultCodeEnum.PARAM_IS_ERROR);
            // 同步修改文件夹名称
            File file = new File(nginxFilePath + "/" + temp.getDatasetId() + "/" + temp.getClassifyName());
            FileUtil.rename(file, classifyName, true);
            // 数据转换，存入数据库
            classify = classifyConvert.classifySaveDTO2Po(dto);
            this.updateById(classify);

        }
        // 准备返回值
        return classifyConvert.po2ClassifyEchoVO(classify);
    }

    @Override
    @Transactional
    @Caching(
            evict = {
                    @CacheEvict(key = "T(com.briup.pai.common.constant.DatasetConstant).DATASET_CLASSIFY_CACHE_PREFIX+':'+#classifyId"),
                    @CacheEvict(key = "T(com.briup.pai.common.constant.CommonConstant).DETAIL_CACHE_PREFIX+':'+#datasetId")
            }
    )
    public void removeClassifyById(Integer datasetId, Integer classifyId) {
        // 参数校验
        BriupAssert.requireNotNull(datasetId, ResultCodeEnum.PARAM_IS_ERROR);
        BriupAssert.requireNotNull(classifyId, ResultCodeEnum.PARAM_IS_ERROR);

        // 数据集不能已经用于模型的训练、优化、评估（此功能后续学完模型模块再添加）
        BriupAssert.requireNotIn(
                datasetId,
                modelServiceImpl.getDatasetIdsUsed(),
                ResultCodeEnum.DATA_CAN_NOT_DELETE
        );

        // 分类必须存在
        Classify classify = BriupAssert.requireNotNull(
                this,
                Classify::getId,
                classifyId, ResultCodeEnum.DATA_NOT_EXIST);
        // 数据集id必须一致
        BriupAssert.requireEqual(
                classify.getDatasetId(),
                datasetId,
                ResultCodeEnum.PARAM_IS_ERROR);
        // 删除的时候连带着分类一起删除
        LambdaQueryWrapper<Entity> lqw = new LambdaQueryWrapper<>();
        lqw.eq(Entity::getClassifyId,classifyId);
        List<Integer> entityIds = entityService.list(lqw).stream().map(Entity::getId).toList();

        entityService.removeBatchByIds(entityIds);
        this.removeById(classifyId);
        // 同步删除文件夹
        FileUtil.del(nginxFilePath + "/" + datasetId + "/" + classify.getClassifyName());
    }

    @Override
    @Cacheable(key = "T(com.briup.pai.common.constant.DatasetConstant).DATASET_CLASSIFY_CACHE_PREFIX+':'+#classifyId")
    public ClassifyEchoVO getClassifyById(Integer classifyId) {
        return classifyConvert.po2ClassifyEchoVO(BriupAssert.requireNotNull(
                this,
                Classify::getId,
                classifyId,
                ResultCodeEnum.DATA_NOT_EXIST));
    }
}